#pragma once
#include <queue>
#include "mutex.hpp"
#include "cond.hpp"

namespace BlockQueueModual
{
    using namespace std;
    using namespace MutexModel;
    using namespace CondModul;

    template <typename T>
    class BlockQueue
    {
        bool IsFull(){return q.size() == _cap;}
        bool IsEmpty(){return q.empty();}
    public:
        BlockQueue(int cap=10) : _cap(cap)
        {
        }
        void Push(T& data)
        {
            LockGuard lock(_mutex);
            while (IsFull())
            {
                cout << "生产者进入等待……" << endl;
                _pwait_num++;
                _productor_cond.Wait(_mutex);
                _pwait_num--;
                cout << "生产者被唤醒……" << endl;
            }
            q.push(data);
            if (_cwait_num)
            {
                cout << "叫醒消费者……" << endl;
                _consumer_cond.Notify();
            }
        }
        void Pop(T *out)
        {
            LockGuard lock(_mutex);
            while (IsEmpty())
            {
                cout << "消费者进入等待……" << endl;
                _cwait_num++;
                _consumer_cond.Wait(_mutex);
                _cwait_num--;
                cout << "消费者被唤醒……" << endl;
            }
            *out = q.front();
            q.pop();

            if (_pwait_num)
            {
                cout << "叫醒生产者……" << endl;
                _productor_cond.Notify();
            }
        }
        ~BlockQueue()
        {
        }

    private:
        queue<T> q;
        int _cap;
        Mutex _mutex;
        Cond _productor_cond;
        Cond _consumer_cond;

        int _cwait_num=0;
        int _pwait_num=0;
    };

}